﻿using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
using Fall.Core.UnitOfWorks;
using System;
using System.Linq;
using System.Threading.Tasks;

namespace Fall.EntityFrameworkCore.UnitOfWorks
{
    /// <summary>
    /// 工作单元接口
    /// </summary>
    public class UnitOfWork<TDbContext> : IUnitOfWork<TDbContext>
        where TDbContext:DbContext
    {
        private TDbContext DbContext;
        private IDbContextTransaction DbTransaction { get; set; }
        public UnitOfWork(TDbContext dbContext)
        {
            DbContext = dbContext ?? throw new ArgumentException("数据库上下文不存在");
        }
        private void CheckDbContext()
        {
            if (DbContext == null)
                throw new Exception("事务开启失败，上下文不存在，确认 Repository<TEntity>(bool setDbContext = false)，设置setDbContext为True");
        }

        public async Task BeginTransactionAsync()
        {
            CheckDbContext();
            DbTransaction = await DbContext.Database.BeginTransactionAsync();
        }

        public void BeginTransaction()
        {
            CheckDbContext();
            DbTransaction = DbContext.Database.BeginTransaction();
        }


        public int Commit()
        {
            CheckDbContext();
            try
            {
                var result = DbContext.SaveChanges();
                if (DbTransaction != null) DbTransaction.Commit();
                return result;
            }
            catch (Exception ex)
            {
                CleanChanges(DbContext);
                if (DbTransaction != null) DbTransaction.Rollback();
                throw new Exception($"Commit 异常：{ex.InnerException}/r{ ex.Message}");
            }
        }

        public async Task<int> CommitAsync()
        {
            CheckDbContext();
            try
            {
                var result = await DbContext.SaveChangesAsync();
                if (DbTransaction != null) DbTransaction.Commit();
                return result;
            }
            catch (Exception ex)
            {
                CleanChanges(DbContext);
                if (DbTransaction != null) DbTransaction.Rollback();
                throw new Exception($"Commit 异常：{ex.InnerException}/r{ ex.Message}");
            }
        }

        public void Dispose()
        {
            if (DbTransaction != null) DbTransaction.Dispose();
            if (DbContext != null) DbContext.Dispose();
            GC.SuppressFinalize(this);
        }

        /// <summary>
        ///     操作失败，还原跟踪状态
        /// </summary>
        /// <param name="context"></param>
        private static void CleanChanges(DbContext context)
        {
            var entries = context.ChangeTracker.Entries().ToArray();
            for (int i = 0; i < entries.Length; i++)
            {
                entries[i].State = EntityState.Detached;
            }
        }
    }
}
